﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace HQHS.Library.Common.Factory
{
    public class Factory<T>
    {
        // Methods
        public static T Create(params object[] parameterValues)
        {
            return Factory<T>.Create(typeof(T), parameterValues);
        }

        public static T Create(IObjectId id)
        {
            return Factory<T>.Create(typeof(T), id);
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public static T Create(Type type, object[] parameterValues)
        {
            T local2 = default(T);
            Type outType = null;
            if (!ClassRegistry.TryRemapType(type, ref outType))
            {
                outType = type;
            }
            if (typeof(ISingleton).IsAssignableFrom(outType))
            {
                local2 = Factory<T>.CreateRegistered(outType, new ObjectId(type));
            }
            else
            {
                local2 = Factory<T>.CreateInstance(outType, parameterValues);
            }
            ClassRegistry.RegisterCreate();
            return local2;
        }

        public static T Create(Type type, IObjectId id)
        {
            T local2 = default(T);
            Type outType = null;
            if (!ClassRegistry.TryRemapType(type, ref outType))
            {
                outType = type;
            }
            local2 = Factory<T>.CreateRegistered(outType, id);
            ClassRegistry.RegisterCreate();
            return local2;
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected static T CreateInstance(Type type, object[] parameterValues)
        {
            T local2;
            ConstructorInfo constructor = ClassRegistry.GetConstructor(type, parameterValues);
            if (constructor == null)
            {
                throw new Exception("Unable to find an apropriate constructor for " + type.FullName + ".");
            }
            try
            {
                local2 = (T)(RuntimeHelpers.GetObjectValue(RuntimeHelpers.GetObjectValue(constructor.Invoke(parameterValues))));
            }
            catch (Exception ex)
            {
                throw new Exception("Factory was unable to instantiate a " + type.FullName + " as a " + typeof(T).FullName + " using constructor " + constructor.ToString() + ".");
            }
            return local2;
        }

        [EditorBrowsable(EditorBrowsableState.Advanced)]
        protected static T CreateRegistered(Type type, IObjectId objectId)
        {
            object objectValue = null;
            objectValue = RuntimeHelpers.GetObjectValue(ObjectRegistry.GetObject(objectId));
            if (objectValue == null)
            {
                Type type2 = type;
                lock (type2)
                {
                    objectValue = RuntimeHelpers.GetObjectValue(ObjectRegistry.GetObject(objectId));
                    if (objectValue == null)
                    {
                        foreach (FieldInfo info in type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
                        {
                            try
                            {
                                info.GetValue(null);
                            }
                            catch (Exception exception1)
                            {
                                throw exception1;
                            }
                            break;
                        }
                        objectValue = RuntimeHelpers.GetObjectValue(ObjectRegistry.GetObject(objectId));
                        if (objectValue == null)
                        {
                            objectValue = Factory<T>.CreateInstance(type, new object[0]);
                            ObjectRegistry.AddObject(objectId, RuntimeHelpers.GetObjectValue(objectValue));
                            if (typeof(IRegisteredObjectBootstrap).IsAssignableFrom(type))
                            {
                                ((IRegisteredObjectBootstrap)objectValue).OnRegistrationComplete();
                            }
                        }
                    }
                }
            }
            return (T)(RuntimeHelpers.GetObjectValue(objectValue));
        }
    }
}